﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using K9Nano.Caching;
using K9Nano.Dependency;
using K9Nano.Share.Extensions;
using Microsoft.Extensions.Logging;

namespace K9Nano.Authorization
{
    public class DefaultRbacManager : IRbacManager, IScopedDependency
    {
        protected readonly IRoleResourceStore Store;
        protected readonly IApiResourceProviderFactory ApiResourceProviderFactory;
        protected readonly ITypedCache<ApiResourceRoles> Cache;
        protected readonly ILogger Logger;

        public DefaultRbacManager(IRoleResourceStore store,
            IApiResourceProviderFactory providerFactory,
            ITypedCache<ApiResourceRoles> cache, 
            ILogger<DefaultRbacManager> logger)
        {
            Store = store;
            ApiResourceProviderFactory = providerFactory;
            Cache = cache;
            Logger = logger;
        }

        public virtual async ValueTask<IReadOnlyList<IApiResource>> GetResourcesInRolesAsync(string api, IEnumerable<string> roles, CancellationToken cancellation)
        {
            var resources = await Store.GetResourcesAsync(api, roles, cancellation);
            var provider = ApiResourceProviderFactory.Create(api);
            return provider.Resources.Where(x => resources.Contains(x.PrimaryId)).ToList();
        }

        public virtual async ValueTask<IReadOnlyList<string>> GetRolesInResourceAsync(string api, string resource,
            CancellationToken cancellation)
        {
            var key = GetCacheKey(api, resource);
            var result = Cache.Get(key);
            if (result == null)
            {
                var roles = await Store.GetRolesInResourceAsync(api, resource, cancellation);
                result = new ApiResourceRoles {Roles = roles};
                Cache.Add(key, result);
            }
            return result.Roles;
        }

        public virtual async ValueTask RemoveRoleFromResourcesAsync(string api, string resource, IEnumerable<string> roles,
            CancellationToken cancellation)
        {
            await Store.RemoveRoleFromResourcesAsync(api, resource, roles, cancellation);
            var key = GetCacheKey(api, resource);
            Cache.Remove(key);
        }

        public virtual async ValueTask AddRoleToResourcesAsync(string api, string role, IEnumerable<string> resources, CancellationToken cancellation)
        {
            var apiList = resources.AsList();

            await Store.AddRoleToResourcesAsync(api, role, apiList, cancellation);

            foreach (var resource in apiList)
            {
                var key = GetCacheKey(api, resource);
                try
                {
                    Cache.Remove(key);
                }
                catch (System.Exception ex)
                {
                    Logger.LogError(ex, $"清除缓存失败: {key}");
                }
            }
        }

        protected virtual string GetCacheKey(string api, string apiResource) => $"{api}:{apiResource}";
    }
}